package zd

import (
	"context"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"unicode"
)

// fixMapToStruct 修正返回结果集的 Key 与 struct 字段名一致
//
//	@param ctx
//	@param entity IEntity 实体指针
//	@param rows []map[string]any 结果集
//	@return []map map[string]any 结果集
func fixMapToStruct(ctx context.Context, entity IEntity, rows ...map[string]any) []map[string]any {
	if len(rows) == 0 {
		return nil
	}
	// 根据 T 将 map 转为对应的字段名
	structColumns := GetColumns(ctx, entity)
	mapColumn := make(map[string]string)
	for _, v := range structColumns {
		mapColumn[v.ColumnName] = v.StructFieldName
	}

	// 第一条记录, key 值 对应
	for key := range rows[0] {
		if _, ok := mapColumn[key]; ok { // map的key为数据库字段名: 忽略
			continue
		}
		// 替换列名为 struct 字段名(如: distinct xxx)
		for _, v := range structColumns {
			if strings.Contains(key, v.ColumnName) { // 匹配到数据库字段名: 替换为 struct 字段名
				mapColumn[key] = strings.ReplaceAll(key, v.ColumnName, v.StructFieldName)
			}
		}
	}
	for i, row := range rows {
		for key, v := range row {
			if structName, ok := mapColumn[key]; ok { // map的key为数据库字段名: 替换为 struct 字段名
				delete(rows[i], key)
				rows[i][structName] = v
			}
		}
	}
	return rows
}

// fixMapToDBName 将 map 的 keys 转换为与数据库字段名一致
//
//	@param ctx zorm 生成的 ctx
//	@param m map 源 map
//	@return mm 返回 map
func fixMapToDBName(ctx context.Context, entity IEntity, m map[string]any) map[string]any {
	cols := GetColumns(ctx, entity)
	// 有适配的 key, 则替换为数据库字段名, 否则保留
	for _, col := range cols {
		if v, ok := m[col.StructFieldName]; ok {
			delete(m, col.StructFieldName)
			m[col.ColumnName] = v
		}
	}
	return m
}

// StructToMap 结构体转 map(key Camel大驼峰格式)
//
//	@param ctx
//	@param t any 不能是指针!!!
//	@param filterZeroField bool 过滤0值字段(bool-false, int-0, string-"", nil)
//	@param filterAutoIncrementField bool 过滤自增字段(用于 insert/update)
//	@return mm
func StructToMap(ctx context.Context, t any, filterZeroField, filterAutoIncrementField bool) (mm map[string]any) {
	mm = make(map[string]any)
	//TypeOf会返回目标数据的类型，比如int/float/struct/指针等
	sType := reflect.TypeOf(t)
	sVal := reflect.ValueOf(t)
	if sType.Kind() == reflect.Ptr {
		// 传入的inStructPtr是指针，需要.Elem()取得指针指向的value
		sType = sType.Elem()
		sVal = sVal.Elem()
	}
	for i := 0; i < sType.NumField(); i++ {
		typ := sType.Field(i)          // 字段的数据类型
		if typ.Type.Name() != "Time" { // struct 为时间类型*不*处理
			// 判断字段是否为结构体类型，或者是否为指向结构体的指针类型
			if sVal.Field(i).Kind() == reflect.Struct || (sVal.Field(i).Kind() == reflect.Ptr && sVal.Field(i).Elem().Kind() == reflect.Struct) { // 组合 struct
				for k, v := range StructToMap(ctx, sVal.Field(i).Interface(), filterZeroField, filterAutoIncrementField) {
					mm[k] = v
				}
				continue
			}
		}
		if filterZeroField && sVal.Field(i).IsZero() { // 0 值不处理
			continue
		}
		if typ.Name[0] >= 'a' && typ.Name[0] <= 'z' { // 非公开字段不处理
			continue
		}
		val := sVal.Field(i).Interface()
		switch typ.Type.Kind() {
		case reflect.Ptr, reflect.UnsafePointer, reflect.Uintptr:
			val = sVal.Field(i).Elem()
			continue
		case reflect.Uint8: // 自定义 enum
			val = fmt.Sprintf("%c", val)
			if val == "\x00" {
				if !filterZeroField {
					val = "0" // enum 未赋值,用 '0' 做初值(解决 new(T)的默认值问题)
				} else {
					continue // 未赋值不处理
				}
			}
		}
		mm[typ.Name] = val
	}
	// 删除自增列
	if filterAutoIncrementField {
		for _, v := range getColumns(ctx, t) {
			if v.AutoIncrement { // 自增列由系统处理
				delete(mm, v.StructFieldName)
			}
		}
	}
	return mm
}

// fixColumnName postgresql 字段名转为 snake 格式, 其他数据库转为大写
//
//	@param ctx
//	@param colName
//	@return string
func fixColumnName(ctx context.Context, colName string) string {
	if ctx.Value(PG) != nil {
		return toSnakeString(colName)
	}
	return strings.ToUpper(colName)
}

// toSnakeString 转换为蛇形字串
//
//	@param camel (如: "CTPAccount", "DCE", "CZCEUser", "UserID", "SimpleExample")
//	@return string
func toSnakeString(s string) string {
	var result strings.Builder
	var buffer strings.Builder
	var prevRune rune

	for i, r := range s {
		if unicode.IsUpper(r) {
			// 检查当前字符和前一个字符是否组成一个已知的缩写词
			if i > 0 && (unicode.IsLower(prevRune) || (i+1 < len(s) && unicode.IsLower(rune(s[i+1])))) {
				// 如果前一个是小写字母或后一个是小写字母，则插入下划线
				if buffer.Len() > 0 {
					result.WriteString(strings.ToLower(buffer.String()))
					buffer.Reset()
				}
				result.WriteRune('_')
			}
			buffer.WriteRune(r)
		} else {
			// 当遇到小写字母时，检查是否有未处理的大写字母序列
			if buffer.Len() > 0 {
				currentWord := buffer.String()
				result.WriteString(strings.ToLower(currentWord))
				buffer.Reset()
			}
			result.WriteRune(r)
		}
		prevRune = r
	}

	// 处理剩余的缓冲区内容
	if buffer.Len() > 0 {
		currentWord := buffer.String()
		result.WriteString(strings.ToLower(currentWord))
	}

	return result.String()
}

// FixQryParams qry参数(map[string]string) 转换为 T 对应类型的 where条件(map[string]any)
//
//	@param ctx
//	@param entity
//	@param qry
//	@return where
//	@return err
func FixQryParams(ctx context.Context, entity IEntity, qry map[string]string) (where map[string]any, err error) {
	mv := StructToMap(ctx, entity, false, false)
	where = make(map[string]any)
	for k, v := range qry {
		where[k] = v
		if tmp, ok := mv[k]; ok {
			switch reflect.TypeOf(tmp).Kind() {
			case reflect.Float64:
				where[k], err = strconv.ParseFloat(v, 64)
			case reflect.Bool:
				where[k], err = strconv.ParseBool(v)
			case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64:
				where[k], err = strconv.Atoi(v)
			}
		}
	}
	return
}
